package com.wang.tool.trace;

import com.wang.tool.common.ErrorInfo;
import com.wang.tool.monitor.InvokeTemplate;
import com.wang.tool.monitor.LinkLogUtil;
import com.wang.tool.monitor.core.MonitorInfo;
import com.wang.tool.monitor.template.MonitorInvoker;

import java.util.function.BiConsumer;
import java.util.function.Function;

public class TraceTemplate {

    /**
     * 执行getOrCreate
     *
     * @param action 业务执行逻辑
     * @param <T>
     * @return
     */
    public static <T> T invoke(Function<TraceContext, T> action) {
        TraceContext current = TraceHolder.getCurrent();
        try {
            TraceContext context = TraceHolder.getOrCreateTrace(current);
            return action.apply(context);
        } catch (Throwable e) {
            throw e;
        } finally {
            TraceHolder.set(current);
        }
    }


    /**
     * 下一节点的执行
     *
     * @param action
     * @param <T>
     * @return
     */
    public static <T> T invokeWithNext(Function<TraceContext, T> action) {
        TraceContext current = TraceHolder.getCurrent();
        try {
            TraceContext context = TraceHolder.nexTrace(current);
            return action.apply(context);
        } catch (Throwable e) {
            throw e;
        } finally {
            TraceHolder.set(current);
        }
    }

    public static <R> R monitorWithNext(String group, String biz, String action, MonitorInvoker<R> invoker) {
        return monitorWithNext(group, biz, action, invoker, null);
    }

    /**
     * 下一节点的执行带monitor
     *
     * @param group
     * @param biz
     * @param action
     * @param invoker
     * @param <R>
     * @return
     */
    public static <R> R monitorWithNext(String group, String biz, String action, MonitorInvoker<R> invoker, BiConsumer<TraceContext, MonitorInfo<R>> before) {
        TraceContext current = TraceHolder.getCurrent();
        try {
            TraceContext context = TraceHolder.nexTrace(current);
            return InvokeTemplate.monitor(group, biz, action, new MonitorInvoker<R>() {
                @Override
                public Object[] getParams() {
                    return invoker.getParams();
                }

                @Override
                public R handleError(Throwable e, ErrorInfo error) {
                    return invoker.handleError(e, error);
                }

                @Override
                public void weaveOnAfter(MonitorInfo<R> monitorInfo) throws Throwable {
                    invoker.weaveOnAfter(monitorInfo);
                }

                @Override
                public void weaveOnBefore(MonitorInfo<R> monitorInfo) throws Throwable {
                    if (context != null) {
                        monitorInfo.addDigest(MonitorInfo.REQ_ID, context.buildReqId());
                        monitorInfo.addDigest(MonitorInfo.TIME, MonitorInfo.formatTimeWithZone(monitorInfo.getBeginTime()));
                    }
                    if (before != null) {
                        try {
                            before.accept(context, monitorInfo);
                        } catch (Throwable e) {
                            LinkLogUtil.newTagLog("nextTraceWithMonitorBeforeError").v1(monitorInfo.getGroup()).v2(monitorInfo.getBiz()+"."+monitorInfo.getAction()).addAttachment(MonitorInfo.ERROR,e).log();
                        }
                    }
                    invoker.weaveOnBefore(monitorInfo);
                }

                @Override
                public R internalCall() throws Throwable {
                    return invoker.internalCall();
                }
            });
        } catch (Throwable e) {
            throw e;
        } finally {
            TraceHolder.set(current);
        }
    }
}
